/*!
 * @file ooview.cpp
 * @author Rocco Martino
 */
/***************************************************************************
 *   Copyright (C) 2015 by Rocco Martino                                   *
 *   martinorocco@gmail.com                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License as        *
 *   published by the Free Software Foundation; either version 2.1 of the  *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

/* ======================================================================= */
/* ....................................................................... */
#include <ooViewer/Viewer>

#include <osgViewer/ViewerEventHandlers>

#include <osgDB/ReadFile>

#include <osgUtil/Optimizer>

#include <OpenThreads/Thread>

#include <osgGA/TrackballManipulator>
#include <osgGA/FlightManipulator>
#include <osgGA/DriveManipulator>
#include <osgGA/KeySwitchMatrixManipulator>
#include <osgGA/StateSetManipulator>
#include <osgGA/AnimationPathManipulator>
#include <osgGA/TerrainManipulator>
#include <osgGA/SphericalManipulator>

#include <osgODE/Notify>
/* ....................................................................... */
/* ======================================================================= */









/* ======================================================================= */
// from osgviewer.cpp
/* ....................................................................... */
int
main( int argc, char** argv )
{
    OpenThreads::Thread::Init() ;



    osg::ArgumentParser arguments( &argc, argv ) ;


    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the standard o.O example which loads and visualises 3d models.");
    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
    arguments.getApplicationUsage()->addCommandLineOption("--oo-help", "write out ooview-specific options");
    arguments.getApplicationUsage()->addCommandLineOption("--oo-manipulators", "enable the camera manipulators");
    arguments.getApplicationUsage()->addCommandLineOption("--oo-stats <key>", "bind a stats handler to the given key");
    arguments.getApplicationUsage()->addCommandLineOption("--oo-silent", "nothing to stdout");
    arguments.getApplicationUsage()->addCommandLineOption("--oo-near-far <znear, zfar>", "set near and far clipping plane");
    arguments.getApplicationUsage()->addCommandLineOption("--oo-fov <fov>", "set the field of view");
    arguments.getApplicationUsage()->addCommandLineOption("--oo-setup <path>", "use a shared object (dll) to perform an initial setup. The signature of the called function is: void setup(void* data) where \"data\" is the loaded osg::Node");


    if( arguments.read("--oo-help") ) {
        arguments.getApplicationUsage()->write(std::cout);
        return 1 ;
    }


    ooViewer::Viewer    viewer( arguments ) ;

    unsigned int helpType = 0;
    if ((helpType = arguments.readHelpType()))
    {
        arguments.getApplicationUsage()->write(std::cout, helpType);
        return 1;
    }

    // report any errors if they have occurred when parsing the program arguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
        return 1;
    }

    if (arguments.argc()<=1)
    {
        arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
        return 1;
    }


    while(arguments.read("--oo-silent"))
    {
        psSetLogLevel( LOG_LEVEL_NONE ) ;
    }


    
    bool use_manipulators = false;
    while(arguments.read("--oo-manipulators"))
    {
        use_manipulators = true ;
    }

    if( use_manipulators ) {
    // set up the camera manipulators.
        osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;

        keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );
        keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() );
        keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() );
        keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new osgGA::TerrainManipulator() );
        keyswitchManipulator->addMatrixManipulator( '5', "Orbit", new osgGA::OrbitManipulator() );
        keyswitchManipulator->addMatrixManipulator( '6', "FirstPerson", new osgGA::FirstPersonManipulator() );
        keyswitchManipulator->addMatrixManipulator( '7', "Spherical", new osgGA::SphericalManipulator() );

        viewer.setCameraManipulator( keyswitchManipulator.get() );
    }




    std::string     stats_key ;
    while(arguments.read("--oo-stats", stats_key))
    {
        if( stats_key.size() == 0 ) {
            stats_key = "s" ;
        }

        osgViewer::StatsHandler*    stats = new osgViewer::StatsHandler() ;

        stats->setKeyEventTogglesOnScreenStats( stats_key[0] ) ;

        viewer.addEventHandler( stats ) ;
    }




    double  znear, zfar ;
    while(arguments.read("--oo-near-far", znear, zfar))
    {
        double  fov, aspect, tmp1, tmp2 ;

        viewer.getCamera()->getProjectionMatrixAsPerspective(fov, aspect, tmp1, tmp2) ;

        viewer.getCamera()->setComputeNearFarMode( osg::Camera::DO_NOT_COMPUTE_NEAR_FAR ) ;
        viewer.getCamera()->setProjectionMatrixAsPerspective(fov, aspect, znear, zfar) ;
    }




    double  fov ;
    while(arguments.read("--oo-fov", fov))
    {
        double  tmp, aspect, znear, zfar ;

        viewer.getCamera()->getProjectionMatrixAsPerspective(tmp, aspect, znear, zfar) ;

        viewer.getCamera()->setProjectionMatrixAsPerspective(fov, aspect, znear, zfar) ;
    }



    std::string     setup_path = "" ;
    while( arguments.read("--oo-setup", setup_path) ) ;


    osg::ref_ptr<osgDB::DynamicLibrary> setup = NULL ;

    if( setup_path != "" ) {
        setup = osgDB::DynamicLibrary::loadLibrary( setup_path ) ;
    }




    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
        return 1;
    }



    

    // load the data
    osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
    if (!loadedModel)
    {
        std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;
        return 1;
    }

    // any option left unread are converted into errors to write out later.
    arguments.reportRemainingOptionsAsUnrecognized();

    // report any errors if they have occurred when parsing the program arguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
        return 1;
    }

    // optimize the scene graph, remove redundant nodes and state etc.
    osgUtil::Optimizer optimizer;
    optimizer.optimize(loadedModel.get());



    if( setup.valid() ) {

        typedef void (*setup_proto)(void* data) ;

        setup_proto setup_fn = (setup_proto) setup->getProcAddress("setup") ;

        if( setup_fn ) {
            setup_fn( loadedModel.get() ) ;
        }
    }





    viewer.setSceneData( loadedModel.get() );

    viewer.realize();

    return viewer.run();
}
/* ....................................................................... */
/* ======================================================================= */